#include "v_cpu.h"
#include "fetch_decode.h"
#include <stddef.h>

#include <stdio.h>

unsigned int sregs_resolve16_base[8] = {
	GPR_BX,
	GPR_BX,
	GPR_BP,
	GPR_BP,
	GPR_SI,
	GPR_DI,
	GPR_BP,
	GPR_BX
};

unsigned int sregs_resolve16_index[8] = {
	GPR_SI,
	GPR_DI,
	GPR_SI,
	GPR_DI,
	GPR_NIL,
	GPR_NIL,
	GPR_NIL,
	GPR_NIL
};

unsigned int sregs_mod00_rm16[8] = {
	SREG_DS,
	SREG_DS,
	SREG_SS,
	SREG_SS,
	SREG_DS,
	SREG_DS,
	SREG_DS,
	SREG_DS
};

unsigned int sregs_mod01or10_rm16[8] = {
	SREG_DS,
	SREG_DS,
	SREG_SS,
	SREG_SS,
	SREG_DS,
	SREG_DS,
	SREG_SS,
	SREG_DS
};

unsigned int sregs_mod00_rm32[8] = {
	SREG_DS,
	SREG_DS,
	SREG_DS,
	SREG_DS,
	SREG_NIL,
	SREG_DS,
	SREG_DS,
	SREG_DS
};

unsigned int sregs_mod01or10_rm32[8] = {
	SREG_DS,
	SREG_DS,
	SREG_DS,
	SREG_DS, 
	SREG_NIL, // Escape to SIB byte
	SREG_SS,
	SREG_DS,
	SREG_DS
};

// Decode for SIB byte
unsigned int sregs_mod00_base32[8] = {
	SREG_DS,
	SREG_DS,
	SREG_DS,
	SREG_DS,
	SREG_SS,
	SREG_DS,
	SREG_DS,
	SREG_DS
};

unsigned int sregs_mod01or10_base32[8] = {
	SREG_DS,
	SREG_DS,
	SREG_DS,
	SREG_DS,
	SREG_SS,
	SREG_SS,
	SREG_DS,
	SREG_DS
};

// All avm supported opcode.
ia_opcode_table avm_opcode_table[] = {
	#define avm_define_opcode(index, execute1, execute2) { execute1, execute2 },
	#include "ia_opcode.h"
};
#undef avm_define_opcode

static const ia_opcode_info avm_opcode_info32R[512 * 2] = {
	// 16-bit instructions for 1st 512 entries
	/* 00 /wr */ { 0, IA_ADD_EbGbR },
	/* 01 /wr */ { 0, IA_ADD_EwGwR },
	/* 02 /wr */ { 0, IA_ADD_GbEbR },
	/* 03 /wr */ { 0, IA_ADD_GwEwR },
	/* 04 /wr */ { AVM_IMMEDIATE_Ib, IA_ADD_ALIb },
	/* 05 /wr */ { 0, 0 },
	/* 06 /wr */ { 0, 0 },
	/* 07 /wr */ { 0, 0 },
	/* 08 /wr */ { 0, 0 },
	/* 09 /wr */ { 0, 0 },
	/* 10 /wr */ { 0, 0 },
	/* 11 /wr */ { 0, 0 },
	/* 12 /wr */ { 0, 0 },
	/* 13 /wr */ { 0, 0 },
	/* 14 /wr */ { 0, 0 },
	/* 15 /wr */ { 0, 0 },
	/* 16 /wr */ { 0, 0 },
	/* 17 /wr */ { 0, 0 },
	/* 18 /wr */ { 0, 0 },
	/* 19 /wr */ { 0, 0 },
	/* 1A /wr */ { 0, 0 },
	/* 1B /wr */ { 0, 0 },
	/* 1C /wr */ { 0, 0 },
	/* 1D /wr */ { 0, 0 },
	/* 1E /wr */ { 0, 0 },
	/* 1F /wr */ { 0, 0 },
	/* 20 /wr */ { 0, 0 },
	/* 21 /wr */ { 0, 0 },
	/* 22 /wr */ { 0, 0 },
	/* 23 /wr */ { 0, 0 },
	/* 24 /wr */ { 0, 0 },
	/* 25 /wr */ { 0, 0 },
	/* 26 /wr */ { 0, 0 },
	/* 27 /wr */ { 0, 0 },
	/* 28 /wr */ { 0, 0 },
	/* 29 /wr */ { 0, 0 },
	/* 2A /wr */ { 0, 0 },
	/* 2B /wr */ { 0, 0 },
	/* 2C /wr */ { 0, 0 },
	/* 2D /wr */ { 0, 0 },
	/* 2E /wr */ { 0, 0 },
	/* 2F /wr */ { 0, 0 },
	/* 30 /wr */ { 0, 0 },
	/* 31 /wr */ { 0, 0 },
	/* 32 /wr */ { 0, 0 },
	/* 33 /wr */ { 0, 0 },
	/* 34 /wr */ { 0, 0 },
	/* 35 /wr */ { 0, 0 },
	/* 36 /wr */ { 0, 0 },
	/* 37 /wr */ { 0, IA_AAA },
};

static const ia_opcode_info avm_opcode_info32M[512 * 2] = {
	/* 00 /wm */ { 0, IA_ADD_EbGbM }
} 

/* The following opcodes are undocumented by Intel. */
/* 0xD6 SALC */ 
/* 0xF1 INT1 */
#define X 0

bit8u has_modrm_byte[512] = {
    /*         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F */
	/*         -----------------------------------------------*/
	/* 0x00 */ 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, X,
	/* 0x10 */ 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
	/* 0x20 */ 1, 1, 1, 1, 0, 0, X, 0, 1, 1, 1, 1, 0, 0, X, 0,
	/* 0x30 */ 1, 1, 1, 1, 0, 0, X, 0, 1, 1, 1, 1, 0, 0, X, 0,
	/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 0x60 */ 0, 0, 1, 1, X, X, X, X, 0, 1, 0, 1, 0, 0, 0, 0,
	/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 0x80 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 0xA0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 0xB0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 0xC0 */ 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
	/* 0xD0 */ 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 
	/* 0xE0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	/* 0xF0 */ X, 0, X, X, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1
    /*         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F */

};

avm_addr v_cpu::resolve16_base_index(instruction *i) {
	return (bit16u)(avm_read_16bit_reg(i->sibBase()) + 
			avm_read_16bit_reg(i->sibIndex()) + i->displ16s());
}

avm_addr v_cpu::resolve32_base(instruction *i) {
	return (bit32u)(avm_read_32bit_reg(i->sibBase()) + i->displ32s());
}

avm_addr v_cpu::resolve32_base_index(instruction *i) {
	return (bit32u)(avm_read_32bit_reg(i->sibBase()) + 
			(avm_read_32bit_reg(i->sibIndex()) << i->sibScale()) + 
			i->displ32s());
}
v_cpu::v_cpu() {
	initialize();
}

bit32u v_cpu::get_laddr(bit8u seg, bit32u offset) {
	return segment_regs[seg].sreg_cache.u.segment_descriptor.base + offset;
}

void v_cpu::add_ALIb(instruction *i) {
	bit8u op1, op2, sum;
#ifdef DEBUG
	printf("Before ADD_ALIb, AL = %02X.\n", AL);
#endif
	op1 = AL;
	op2 = i->Ib();
#ifdef DEBUG
	printf("Ib = %02X.\n", i->Ib());
#endif
	sum = op1 + op2;
	AL = sum;
#ifdef DEBUG
	printf("After ADD_ALIb, AL = %02X.\n", AL);
#endif
}

/*void v_cpu::initialize() {
	AL = 0;
}*/

int v_cpu::fetch_decode(const bit8u *iptr, instruction *i, bit8u remaining_in_length) {
	unsigned int remain = (remaining_in_length > 15) ? 15 : remaining_in_length;
	unsigned int ilength = 0, ia_opcode;
	unsigned int attr, offset = 0, prefix, lock = 0, b1, b2, b3;
	avm_bool as_32 = 0, os_32 = 0;
	bit8u seg = SREG_DS,  seg_override, sse_prefix = 0;

	as_32 = os_32 = segment_regs[SREG_CS].sreg_cache.u.segment_descriptor.d_b;

	// Parse prefix
	for ( ; ; ) {
		b1 = *iptr++;
		ilength++;

		if (b1 == 0x0F) { // 2-byte opcode escape
			b1 = 0x0100 | *iptr++;
			ilength++;
#ifdef DEBUG
	printf("An two byte opcode.\n");
#endif
			break;
		}
		else if (b1 == 0xF0) { // Lock
			lock = 0;
		}
		else if ((b1 == 0xF2) || (b1 == 0xF3)) {
			sse_prefix = b1 & 0xF;
			i->set_repUsed(b1 & 3);	
		}
		else if (b1 == 0x66) { // Operand size prefix
#ifdef DEBUG
	printf("Use operand size prefix.\n");
#endif
			os_32 = !as_32;
			offset = os_32 << 9;
			i->set_os32(os_32);
			if (sse_prefix) {
				sse_prefix = SSE_PREFIX_66;	
			}
		}
		else if (b1 == 0x67) { // Address size prefix
			as_32 = !as_32;
			i->set_as32(as_32);
		}
		// 0x2E - 0x65 Segment override prefix
		else if (b1 == 0x2E) { 
			seg_override = SREG_CS;
		}
		else if (b1 == 0x36) {
			seg_override = SREG_SS;
		}
		else if (b1 == 0x3E) {
			seg_override = SREG_DS;
		}
		else if (b1 == 0x26) {
			seg_override = SREG_ES;
		}
		else if (b1 == 0x64) {
			seg_override = SREG_FS;
		}
		else if (b1 == 0x65) {
			seg_override = SREG_GS;
		}
		else {
			break;
		}

		if (ilength <= remain)
			continue;
		else
			return 0;
	}

	bit8u mod = 0, nnn = 0, rm = 0;

	// Here, b1 equals to the primary opcode.
	if (has_modrm_byte[b1]) {
		if (ilength < remain) {
			b2 = *iptr++;
			ilength++;
#ifdef DEBUG
			printf("The byte %02X is followed by a ModRM byte %02X.\n", b1, b2);
#endif
		}
		else {
			return 0;
		}

		/* 7                   0     
		 * +-----+------+------+
		 * | MOD |  NNN |  RM  |
		 * +-----+------+------+
		 * */
		mod = (b2 & 0xC0) >> 6;
		nnn = (b2 >> 3) & 7;
		rm = b2 & 7;

		//i->setMod(mod);
		i->setNnn(nnn);
		i->setModRM(b2);
		
		i->setSibBase(rm);
		i->setSibIndex(GPR_NIL);
		i->setRm(GPR_TMP);
		i->setDispl32s(0);
		
		if (mod == 3) {
#ifdef DEBUG
			printf("[Mod] =  %02X. Jump the addressing parsing.\n", mod);
#endif
			i->setRm(rm);
		}
		else { // mod == 1 or 2, we need to parse the addressing form.
			if (i->as32()) {
#ifdef DEBUG
			printf("Entering 32-bit addressing mode: [base] + displacement8/32 form is assumed.\n");
#endif
				// [base] + displ addressing form assumed.
				i->resolve_modrm = &v_cpu::resolve32_base;
				if (rm != 4) { // No sib byte
#ifdef DEBUG
					printf("[RM] = %02X, No SIB is followed.\n", rm);
#endif
					if (mod == 0x0) {
						if (rm == 5) {
							i->setSibBase(GPR_NIL);
							if ((ilength + 3) < remain) {
								i->setDispl32s(fetch_dword(iptr));
								iptr += 4;
								ilength += 4;
#ifdef DEBUG
								printf("[MOD] = 0, [RM] = 5. [displacement32] only.\n");
								printf("The displacement is: %08X.\n", i->displ32s());
#endif
							}
							else {
								return 0;
							}
						}
						else {
#ifdef DEBUG
							printf("[MOD] = 0. [RM] != 5. [base] only.\n");
#endif
						}
					}
					else if ((mod == 0x01) || (mod == 0x02)) {
						seg = sregs_mod01or10_rm32[rm];
					
						if (mod == 0x01) {
							if (ilength < remain) {
								i->setDispl32s((bit32s)fetch_byte(iptr));
								iptr++;
								ilength++;
#ifdef DEBUG
								printf("[MOD] = 1, [BASE] + disp8 addressing form.\n");
								printf("[Displacement] = %08X.\n", i->displ32s());
#endif
							}
							else {
								return 0;
							}
						}
						else { // mod == 0x02
							if ((ilength + 3) < remain) {
								i->setDispl32s(fetch_dword(iptr));
								iptr += 4;
								ilength += 4;
#ifdef DEBUG
								printf("[MOD] = 2, [BASE] + disp32 addressing form.\n");
								printf("[Displacement] = %08X.\n", i->displ32s());
#endif
							}
							else {
								return 0;
							}
						}
					}
					else {
						// Actually, we never get here.
						return 0;
					}

				}
				else { // with sib byte
					bit8u sib, base, scale, index;
					/* 7     6     3    0
					 * +-----+-----+----+
					 * |Scale|Index|Base|
					 * +-----+-----+----+
					 * */
					if (ilength < remain) {
						sib = *iptr++;
						ilength++;
					}
					else {
						return 0;
					}
#ifdef DEBUG
					printf("[RM] = %02X, with SIB byte: %02X.\n", rm, sib);
#endif
					base = sib & 7;
					scale = (sib >> 6) & 7;
					index = (sib >> 3) & 7;

					i->setSibBase(base);
					i->setSibScale(scale);

					if (index != 4) {
#ifdef DEBUG
						printf("[INDEX] = %02X. Change addressing form to: [base + scale * index] + displacement 8 / 32.\n", index);
#endif
						i->resolve_modrm = &v_cpu::resolve32_base_index;
						i->setSibIndex(index);
					}
					if (mod == 0x00) {
						seg = sregs_mod00_base32[base];
						if (base == 5) {
							i->setSibBase(GPR_NIL);
							if ((ilength + 3) < remain) {
								i->setDispl32s(fetch_dword(iptr));
								iptr += 4;
								ilength += 4;
#ifdef DEBUG
								printf("[MOD] = 0, [BASE] = 5, [scale * index] + displacement32 only.\n");
								printf("The displacement is %08X.\n", i->displ32s());
#endif
							}
							else {
								return 0;
							}
						} 
					}
					else if (mod == 0x01) {
						seg = sregs_mod01or10_base32[base];
					
						if (ilength < remain) {
							i->setDispl32s((bit8s)*iptr++);
							ilength++;
#ifdef DEBUG
							printf("[MOD] = 01, [base + scale * index] + disp8 mode.\n");
							printf("The displacement is: %08X.\n", i->displ32s());
#endif							
						}
						else {
							return 0;
						}
					}
					else if (mod == 0x02) {
						seg = sregs_mod01or10_base32[base];

						if ((ilength + 3) < remain) {
							i->setDispl32s(fetch_dword(iptr));
							iptr += 4;
							ilength += 4;
#ifdef DEBUG
							printf("[MOD] = 02, [base + scale * index] + disp32 mode.\n");
							printf("The displacement is: %08X.\n", i->displ32s());
#endif							
						}
						else {
							return 0;
						}
					}
					else {
						return 0; // Some unknown error
					}
				}
			}
			else { // 16-bit addressing form
				i->resolve_modrm = &v_cpu::resolve16_base_index;
				i->setSibBase(sregs_resolve16_base[rm]);
				i->setSibIndex(sregs_resolve16_index[rm]);
#ifdef DEBUG
				printf("Enter 16-bit addressing mode: [base + index] + displacement8 / 16 is assumed.\n");
#endif
				if (mod == 0) {
					seg = sregs_mod00_rm16[rm];
					if (rm == 6) {
						i->setSibBase(GPR_NIL);
						if ((ilength + 1) < remain) {
							i->setDispl16s(fetch_word(iptr));
							iptr += 2;
							ilength == 2;
#ifdef DEBUG
							printf("[MOD] = %02X, [RM] = %02X, [displacement16] only.\n", mod, rm);
							printf("The displacement is: %04X.\n", i->displ16s());
#endif
						}
						else {
							return 0;
						}
					}
					else {
#ifdef DEBUG
						printf("[MOD] = %02X, [RM] != 6, [base + index] mode.\n", mod);
#endif
					}
				}
				else if (mod == 1) {
					seg = sregs_mod01or10_rm16[rm];
					if (ilength < remain) {
						i->setDispl16s((bit16s)fetch_byte(iptr));
						iptr++;
						ilength++;
#ifdef DEBUG
						printf("[MOD] = %02x, [base + index] + displacement8 mode.\n", mod);
						printf("The displacement is: %04X.\n", i->displ16s());
#endif
					}
					else {
						return 0;
					}
				}
				else if (mod == 2) {
					seg = sregs_mod01or10_rm16[rm];
					if ((ilength + 1) < remain) {
						i->setDispl16s(fetch_word(iptr));
						iptr += 2;
						ilength += 2;;
#ifdef DEBUG
						printf("[MOD] = %02x, [base + index] + displacement16 mode.\n", mod);
						printf("The displacement is: %04X.\n", i->displ16s());
#endif
					}
					else {
						return 0;
					}
				}
				else {
					return 0;
				}
			} // End if (mod == 0)
		} // End if (i->as32())

		const ia_opcode_info *p_ia_info;
		
		if (mod == 3) {
			p_ia_info = &avm_opcode_info32R[b1 + offset];
			attr = avm_opcode_info32R[b1 + offset].attribute;
		}
		else {
			// p_ia_info = &avm_opcode_info32M[b1 + offset];
			// attr = avm_opcode_info32M[b1 + offset].attribute;
		}
		/*while (attr & AVM_GROUPX) {
		
		}*/

		ia_opcode = p_ia_info->ia_opcode;
	}
	else {
		const ia_opcode_info *p_ia_info;
		p_ia_info = &avm_opcode_info32R[b1 + offset];
		attr = avm_opcode_info32R[b1 + offset].attribute;
		ia_opcode = p_ia_info->ia_opcode;
		printf("The instruction %02X doesn't have a ModRm byte.\n", b1);
	} // End if (has_modrm_byte[b1])

	i->modrm_form.Id = 0;
	if (attr) {
		switch (attr) {
			case AVM_IMMEDIATE_Ib:
				if (ilength < remain) {
					i->modrm_form.Ib = *iptr++;
					ilength++;
				}
				break;
			default:
				break;
				//return 0;
		}
	}
	i->execute1 = avm_opcode_table[ia_opcode].exec1;
	i->execute2 = avm_opcode_table[ia_opcode].exec2;
	return ilength;
}


